//============================================================================
// Copyright (C) 2013 Brett R. Jones
//
// Issued to public domain 2013
//
// brettjones1900@gmail.com
// http://www.p2panarchy.com
//============================================================================

#include "VxSktCode.h"
#include "VxSktUtil.h"
#include "VxTimer.h"
#include <memory.h>
#include <stdio.h>

#include "vx_debug.h"
#ifdef WIN_PLATFORM
#include <WinSock2.h>
#include <ws2tcpip.h>
#else
// LINUX
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#endif

#ifdef WIN_PLATFORM
#define EINPROGRESS	WSAEWOULDBLOCK 
#else
#include <fcntl.h>
#endif // WIN_PLATFORM

//============================================================================
//=== constructor ===//
VxSktCode::VxSktCode()
: m_rcLastError(0)
{
	// empty
}
//============================================================================
//=== destructor ===//
VxSktCode::~VxSktCode()
{
}

//============================================================================	
//! used internally to get the remote ip address and set it in the vars
RCODE VxSktCode::getRmtAddress( SOCKET& oSkt, VxInetAddrAndPort& oRetAddr )
{
	// Get the IP address of the the remote side of connection
	struct sockaddr_storage oSktAddr;
    socklen_t iSktAddrLen = sizeof( oSktAddr );
	if( getpeername( oSkt, (struct sockaddr*)&oSktAddr, &iSktAddrLen ) )
	{
		// error occurred
		oRetAddr.setToInvalid();
		return VxGetLastError();
	}
	oRetAddr.setIp(oSktAddr);
	oRetAddr.m_u16Port = oRetAddr.setIp( oSktAddr );
	return 0;	
}

//============================================================================
RCODE VxSktCode::getLclAddress( SOCKET& oSkt, VxInetAddrAndPort& oRetAddr )
{
	// Get the IP address of the the local side of connection
	struct sockaddr_storage oSktAddr;
    socklen_t iSktAddrLen = sizeof( oSktAddr );
	if( getsockname( oSkt, (struct sockaddr*)&oSktAddr, &iSktAddrLen ) )
	{
		// error occurred
		oRetAddr.setToInvalid();
		return VxGetLastError();
	}
	oRetAddr.setIp(oSktAddr);
	oRetAddr.m_u16Port = oRetAddr.setIp( oSktAddr );
	return 0;
}

//============================================================================
//! connect to remote ip
SOCKET VxSktCode::ConnectTo(		VxInetAddress&		oLclIp,
									VxInetAddrAndPort&	oRmtIp,
									const char *		pIpOrUrl,				// remote ip or url
									U16					u16Port,				// port to connect to
									int					iTimeoutMilliSeconds)	// milli seconds before connect attempt times out
{
	SOCKET oSkt;
	struct addrinfo hints, *servinfo, *p;
	int rv;

	memset(&hints, 0, sizeof hints);
	//hints.ai_family = AF_UNSPEC;
	//hints.ai_family = AF_INET
	//hints.ai_family = AF_INET6
	hints.ai_family = AF_INET;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = IPPROTO_TCP;


	char as8Buf[ 16 ];
	sprintf( as8Buf, "%d", u16Port );

	if ((rv = getaddrinfo(pIpOrUrl, as8Buf, &hints, &servinfo)) != 0) 
	{
		fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
		return 1;
	}

	// loop through all the results and connect to the first we can
	for(p = servinfo; p != NULL; p = p->ai_next) 
	{
		if ((oSkt = socket(p->ai_family, p->ai_socktype,
			p->ai_protocol)) == -1) 
		{
				perror("client: socket");
				continue;
		}

		if (connect(oSkt, p->ai_addr, p->ai_addrlen) == -1) 
		{
			VxCloseSktNow(oSkt);
			perror("client: connect");
			continue;
		}
		getRmtAddress( oSkt, oRmtIp );

		break;
	}

	if (p == NULL) 
	{
		fprintf(stderr, "client: failed to connect\n");
		return INVALID_SOCKET;
	}

//	inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr),
//		s, sizeof s);
//	printf("client: connecting to %s\n", s);

	freeaddrinfo( servinfo ); // all done with this structure

	return oSkt;

	return 0;
}
